﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Templates.Models;

namespace Templates.UnitTests
{
    [TestClass]
    public class TemplateFormatterTest
    {
        private readonly TemplateFormatter _formatter = new TemplateFormatter();
        
        [TestMethod]
        public void Match_All_Null()
        {
            var template = new Template();

            string result = _formatter.Write(template);

            Assert.AreEqual(string.Empty, result);
        }

        [TestMethod]
        public void Match_No_Variables()
        {
            var template = new Template { Variables = new List<Variable>() };

            string result = _formatter.Write(template);

            Assert.AreEqual(string.Empty, result);
        }

        [TestMethod]
        public void Match_Perl_Variable_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "$",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                },
                Body = "My favorite expression is $this.is.a.test",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual("My favorite expression is woot!", result);
        }

        [TestMethod]
        public void Match_Brackets_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                },
                Body = "My favorite expression is [this.is.a.test]",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual("My favorite expression is woot!", result);
        }

        [TestMethod]
        public void No_Match_Brackets_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                },
                Body = "My favorite expression is [this.is.not.a.test]",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual(template.Body, result);
        }

        [TestMethod]
        public void Match_Escape_Required_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "^)",
                EndCharacter = "($",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                },
                Body = "My favorite expression is ^)this.is.a.test($",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual("My favorite expression is woot!", result);
        }

        [TestMethod]
        public void No_Match_Escape_Required_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "^)",
                EndCharacter = "($",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                },
                Body = "My favorite expression is ^)this.is.not.a.test($",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual(template.Body, result);
        }

        [TestMethod]
        public void Two_Variables_Match_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                    new Variable { Name = "expression", Value = "expression" },
                },
                Body = "My favorite [expression] is [this.is.a.test]",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual("My favorite expression is woot!", result);
        }

        [TestMethod]
        public void Two_Variables_No_Match_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = false,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                    new Variable { Name = "expression", Value = "expression" },
                },
                Body = "My favorite [Expression] is [this.is.A.test]",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual(template.Body, result);
        }

        [TestMethod]
        public void Two_Variables_No_Match_Case_Insensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = true,
                Variables = new List<Variable>
                {
                    new Variable { Name = "this.is.a.test", Value = "woot!" },
                    new Variable { Name = "expression", Value = "expression" },
                },
                Body = "My favorite expression is woot!",
            };

            string result = _formatter.Write(template);

            Assert.AreEqual(template.Body, result);
        }

        [TestMethod]
        public void Extract_Variable_Case_Sensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = true,
                Body = "My favorite expression is [this.is.a.test]",
            };

            var variables = _formatter.Read(template).Variables;
            var expected = new Variable[] { new Variable { Name = "this.is.a.test" } };
            
            Assert.AreEqual(variables.Count, expected.Length);
            for (int i = 0; i < variables.Count; i++)
            {
                Assert.AreEqual(expected[i].Name, variables[i].Name);
            }
        }

        [TestMethod]
        public void Extract_Variable_Case_InSensitive()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = false,
                Body = "My favorite expression is [this.is.A.test]",
            };

            var variables = _formatter.Read(template).Variables;
            var expected = new Variable[] { new Variable { Name = "this.is.A.test" } };

            Assert.AreEqual(variables.Count, expected.Length);
            for (int i = 0; i < variables.Count; i++)
            {
                Assert.AreEqual(expected[i].Name, variables[i].Name);
            }
        }

        [TestMethod]
        public void Extract_No_Variables()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "[",
                IgnoreCase = false,
                Body = "My favorite expression is [this.is.A.test]",
            };

            var variables = _formatter.Read(template).Variables;
            var expected = new Variable[0];

            Assert.AreEqual(variables.Count, expected.Length);
            for (int i = 0; i < variables.Count; i++)
            {
                Assert.AreEqual(expected[i].Name, variables[i].Name);
            }
        }

        [TestMethod]
        public void Extract_Duplicate_Variables()
        {
            var template = new Template
            {
                StartCharacter = "[",
                EndCharacter = "]",
                IgnoreCase = false,
                Body = "My favorite expression is [test], [test], [test].",
            };

            var variables = _formatter.Read(template).Variables;
            var expected = new Variable[] { new Variable { Name = "test" } };

            Assert.AreEqual(variables.Count, expected.Length);
            for (int i = 0; i < variables.Count; i++)
            {
                Assert.AreEqual(expected[i].Name, variables[i].Name);
            }
        }

        [TestMethod, ExpectedException(typeof(InvalidOperationException))]
        public void Extract_All_Null()
        {
            var template = new Template();

            var variables = _formatter.Read(template).Variables;
            var expected = new Variable[0];

            Assert.AreEqual(variables.Count, expected.Length);
            for (int i = 0; i < variables.Count; i++)
            {
                Assert.AreEqual(expected[i].Name, variables[i].Name);
            }
        }

        [TestMethod]
        public void Extract_All_Null_Except_Variables()
        {
            var template = new Template { StartCharacter = "[", EndCharacter = "]" };

            var variables = _formatter.Read(template).Variables;
            var expected = new Variable[0];

            Assert.AreEqual(variables.Count, expected.Length);
            for (int i = 0; i < variables.Count; i++)
            {
                Assert.AreEqual(expected[i].Name, variables[i].Name);
            }
        }
    }
}
